package io.github.codelifeleno.jdbrelease.execute;

import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.ResourceUtil;
import io.github.codelifeleno.jdbrelease.config.*;
import io.github.codelifeleno.jdbrelease.util.CollectionToMapConverter;
import io.github.codelifeleno.jdbrelease.util.JDBResourceUtils;
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static io.github.codelifeleno.jdbrelease.model.finals.DefaultName.DBScriptConfig_FileName;


/**
 * DBScriptConfig.xml 配置文件加载过程
 * 通过扫描出来的路径将 配置文件截取后的路径一一匹配，能匹配上的就使用同一个配置，不能匹配上的就使用根路径上的配置
 */
public class DBScriptConfigInit {

    private static final Logger log = LoggerFactory.getLogger(DBScriptConfigInit.class);
    /**
     * key 是 文件夹的路径
     * value 是 适配这个路径的数据库脚本运行时属性
     */
    private final ConcurrentHashMap<URI, DBScriptConfig> dbScriptConfigMap = new ConcurrentHashMap<>();

    public ConcurrentHashMap<URI, DBScriptConfig> getDbScriptConfigMap() {
        return dbScriptConfigMap;
    }

    public DBScriptConfig getDbScriptConfig(URI file) throws URISyntaxException {
        DBScriptConfig thisConfig = null;
        for (Map.Entry<URI, DBScriptConfig> entry : dbScriptConfigMap.entrySet()) {
            if (entry.getKey().getPath().equals(file.getPath())) {
                thisConfig = entry.getValue();
                break;
            }
        }
        if (thisConfig == null) {
            thisConfig = getDbScriptConfig(file.getPath());
        }
        return thisConfig;
    }

    public DBScriptConfig getDbScriptConfig(String urlPath) throws URISyntaxException {
        if (urlPath.lastIndexOf("/") == -1) {
            return new DBScriptConfig();
        }
        return getDbScriptConfig(new URI(urlPath.substring(0, urlPath.lastIndexOf("/"))));
    }


    /**
     * 根据实际的路径对配置进行合并
     *
     * @param fileResource 文件或文件夹的路径
     */
    public void configInit(Resource fileResource) throws URISyntaxException, IOException {
        if (fileResource == null) {
            throw new IllegalArgumentException("指定的文件或文件夹 is null");
        }
        List<Resource> resources = JDBResourceUtils.getResources(fileResource.getUrl().toString(), ".*" + DBScriptConfig_FileName);
        Map<URI, Resource> resourceMap = CollectionToMapConverter.toMap(resources, resource -> {
            try {
                if (resource.getUrl() == null) {
                    return null;
                }
                return resource.getUrl().toURI();
            } catch (URISyntaxException e) {
                log.error(e.getMessage(), e);
                return null;
            }
        });
        Set<URI> urls = resourceMap.keySet();
        List<URI> sortedUrls = new LinkedList<>(urls);
        sortedUrls.sort(Comparator.comparing(URI::getPath));

        DBScriptConfig lastConfig = null;
        URI lastUri = null;
        for (URI uri : sortedUrls) {
            uri = uri.normalize();
            URI dbScriptConfigURI = new URI(uri.toString().substring(0, uri.toString().lastIndexOf("/" + DBScriptConfig_FileName)));
            if (lastUri != null && dbScriptConfigURI.getPath().startsWith(lastUri.getPath())) {
                getDbScriptConfigMap().put(dbScriptConfigURI, configWithParent(lastConfig, resourceMap.get(uri)));
            } else {
                getDbScriptConfigMap().put(dbScriptConfigURI, configWithResource(resourceMap.get(uri)));
                lastUri = dbScriptConfigURI;
                lastConfig = getDbScriptConfigMap().get(dbScriptConfigURI);
            }
        }
    }

    public DBScriptConfig configWithParent(DBScriptConfig parentConfig, Resource fileResource) {
        DBScriptConfig childConfig = configWithResource(fileResource);
        if (parentConfig == null) {
            parentConfig = new DBScriptConfig();
        }
        if (childConfig.getSeparator().getEof() == null || StringUtils.isBlank(childConfig.getSeparator().getEof())) {
            childConfig.getSeparator().setEof(parentConfig.getSeparator().getEof());
        }
        if (childConfig.getSeparator().getSimple() == null || StringUtils.isBlank(childConfig.getSeparator().getSimple())) {
            childConfig.getSeparator().setSimple(parentConfig.getSeparator().getSimple());
        }
        if (childConfig.getComments().getSingleLine().getPrefixes() == null || childConfig.getComments().getSingleLine().getPrefixes().isEmpty()) {
            childConfig.getComments().getSingleLine().setPrefixes(parentConfig.getComments().getSingleLine().getPrefixes());
        }
        if (childConfig.getComments().getBlocks() == null || childConfig.getComments().getBlocks().isEmpty()) {
            childConfig.getComments().setBlocks(parentConfig.getComments().getBlocks());
        }
        return childConfig;
    }


    /**
     * 处理过程采用
     * 1. 检查filePath 去找有没有 DBScriptConfig.xml 如果有，则读取文件并覆盖属性 形成新的 config 文件，否则直接使用默认配置
     * 2. 将配置完整的保存到县城访问变量 dbScriptConfigMap 里面
     */
    public void init(JDBReleaseConfig jdbReleaseConfig) throws URISyntaxException, IOException {
        for (String filePath : jdbReleaseConfig.getFileDirPaths()) {
            if (StringUtils.isBlank(filePath)) {
                log.warn("配置异常，指定的文件路径是空的");
                continue;
            }
            Resource resource = ResourceUtil.getResourceObj(filePath);
            configInit((resource));
        }
    }

    /**
     * 通过指定的目录找寻配置文件并初始化文件内容形成配置属性
     *
     * @param dbScriptConfigFile 配置文件的所在路径
     * @return 根据配置文件处理的属性文件对象
     */
    public DBScriptConfig configWithResource(Resource dbScriptConfigFile) {

        if (dbScriptConfigFile == null) {
            throw new NullPointerException("配置文件目录对象实例为空，请修改程序！");
        }
        DBScriptConfig dbScriptConfig = new DBScriptConfig();
        try {
            XMLConfiguration xmlConfig = new FileBasedConfigurationBuilder<>(XMLConfiguration.class)
                    .configure(new Parameters().xml()
                            .setURL(dbScriptConfigFile.getUrl())
                            .setValidating(false)
                            .setSchemaValidation(false))
                    .getConfiguration();
            String simple = xmlConfig.getString("StatementSeparators.Simple");
            String eof = xmlConfig.getString("StatementSeparators.EOF");
            List<String> prefixes = xmlConfig.getList(String.class, "Comments.SingleLine.Prefixes.Prefix");
            List<Block> blockList = new ArrayList<>();
            int maxIndex = xmlConfig.getMaxIndex("Comments.Blocks.Block");
            for (int i = 0; i <= maxIndex; i++) {
                Block block = new Block();
                block.setStart(xmlConfig.getString(String.format("Comments.Blocks.Block(%d).Start", i)));
                block.setEnd(xmlConfig.getString(String.format("Comments.Blocks.Block(%d).End", i)));
                blockList.add(block);
            }
            SingleLine singleLine = new SingleLine(prefixes);

            StatementSeparators separator = new StatementSeparators();
            Comments comments = new Comments(singleLine, blockList);
            separator.setEof(eof);
            separator.setSimple(simple);
            dbScriptConfig.setComments(comments);
            dbScriptConfig.setSeparator(separator);
        } catch (ConfigurationException e) {
            log.error(e.getMessage(), e);
        }
        return dbScriptConfig;
    }
}
